package com.xxx.handler;

import com.xxx.annotation.TimeCost;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class TimeCostAspect {
    private static final Logger logger = LoggerFactory.getLogger(TimeCostAspect.class);

    @Around("@annotation(timeCost)")
    public Object around(ProceedingJoinPoint joinPoint, TimeCost timeCost) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取注解配置
        String methodName = method.getDeclaringClass().getSimpleName() + "." + method.getName();
        String description = timeCost.description().isEmpty() ? methodName : timeCost.description();
        boolean printArgs = timeCost.printArgs();
        TimeCost.TimeUnit timeUnit = timeCost.unit();

        // 打印方法入参（如果需要）
        if (printArgs) {
            logger.info("{} 方法入参: {}", description, Arrays.toString(joinPoint.getArgs()));
        }

        long startTime = System.nanoTime();
        try {
            // 执行目标方法
            return joinPoint.proceed();
        } finally {
            // 计算耗时并转换单位
            long costTime = System.nanoTime() - startTime;
            double convertedTime = convertTimeUnit(costTime, timeUnit);

            // 打印耗时日志
            logger.info("{} 执行耗时: {} {}",
                    description,
                    convertedTime,
                    getTimeUnitName(timeUnit));
        }
    }

    private double convertTimeUnit(long nanos, TimeCost.TimeUnit unit) {
        switch (unit) {
            case NANOSECONDS:  return nanos;
            case MICROSECONDS: return nanos / 1000.0;
            case SECONDS:      return nanos / 1_000_000_000.0;
            default:           return nanos / 1_000_000.0;
        }
    }

    private String getTimeUnitName(TimeCost.TimeUnit unit) {
        switch (unit) {
            case NANOSECONDS:  return "ns";
            case MICROSECONDS: return "μs";
            case SECONDS:      return "s";
            default:           return "ms";
        }
    }
}